﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using CoreEx.Common.Extensions;
using CoreEx.DynamicInvoker;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;

namespace CoreEx.Common.Repository.Implementation
{
    [Implements(typeof(ITrackableListInjector))]
    public class TrackableListInjector : ITrackableListInjector, IInitialize
    {
        private IServiceContainer _serviceContainer;
        
        public void Inject<TTarget>(TTarget target, IStateTracker stateTracker) where TTarget : class
        {
            var properties = target.GetType().GetProperties().Where(p => p.PropertyType.IsEnumerable());

            foreach (var property in properties)
            {
                var elementType = property.PropertyType.GetGenericElementType();
                var listType = typeof(TrackableBindingList<>).MakeGenericType(elementType);
                var originalList = (IList)property.DynamicInvoke(target);
                var trackableList = (IList)Activator.CreateInstance(listType,target, stateTracker);                
                ((IInitialize)trackableList).Initialize(_serviceContainer);
                foreach (var item in originalList)
                {
                    trackableList.Add(item);
                }
                
                var fieldInfo = GetBackingField(target.GetType(), property);
                fieldInfo.DynamicInvoke(target, trackableList);
            }
        }

        protected virtual FieldInfo GetBackingField(Type targetType, PropertyInfo propertyInfo)
        {
            var fields =
                targetType.GetFields(BindingFlags.Instance | BindingFlags.Public |
                                                     BindingFlags.NonPublic);
            var field = fields.Where(f => f.Name.ToUpper().Contains(propertyInfo.Name.ToUpper())).FirstOrDefault();
            if (field == null)
            {
                var fieldName = propertyInfo.Name.GetBestMatch(fields.Select(f => f.Name));
                field = fields.Where(f => f.Name == fieldName).FirstOrDefault();
            }
            return field;
        }

        public void Initialize(IServiceContainer source)
        {
            _serviceContainer = source;
        }
    }
}
